import { Field, InputType, Resolver, Mutation, Arg, Ctx, ObjectType, Query } from 'type-graphql'
import argon2 from 'argon2'
import { User } from '../entities/User'
import { MyContext } from '../types'

@InputType()
class UsernamePasswordInput {
    @Field()
    username: string

    @Field()
    password: string
}

@ObjectType()
class FieldError {
    @Field()
    field: string

    @Field()
    message: string
}

@ObjectType()
class UserResponse {
    @Field(() => [FieldError], { nullable: true })
    errors?: FieldError[]

    @Field(() => User, { nullable: true })
    user?: User
}

@Resolver()
export class UserResolver {
    // 注册
    @Mutation(() => UserResponse)
    async register(
        @Arg('options') options: UsernamePasswordInput,
        @Ctx() { em, req }: MyContext
    ): Promise<UserResponse> {
        if (options.username.length < 2) {
            return {
                errors: [
                    {
                        field: 'username',
                        message: 'length must be greater than or equal to 2'
                    }
                ]
            }
        }

        if (options.password.length < 6) {
            return {
                errors: [
                    {
                        field: 'password',
                        message: 'length must be greater then or equal to 6'
                    }
                ]
            }
        }

        const hashedPassword = await argon2.hash(options.password)
        const user = em.create(User, {
            username: options.username,
            password: hashedPassword
        })
        try {
            await em.persistAndFlush(user)
        } catch (error) {
            if (error.code === '23505') {
                return {
                    errors: [
                        {
                            field: 'username',
                            message: 'username already taken'
                        }
                    ]
                }
            }
        }

        req.session.userId = user.id
        return { user }
    }

    // 登录
    @Mutation(() => UserResponse)
    async login(
        @Arg('options') options: UsernamePasswordInput,
        @Ctx() { em, req }: MyContext
    ): Promise<UserResponse> {
        const user = await em.findOne(User, { username: options.username })
        if (!user) {
            return {
                errors: [
                    {
                        field: 'username',
                        message: 'that username does\'t exist'
                    }
                ]
            }
        }
        const valid = await argon2.verify(user.password, options.password)
        if (!valid) {
            return {
                errors: [
                    {
                        field: 'password',
                        message: 'incrorect password'
                    }
                ]
            }
        }
        req.session.userId = user.id
        return { user }
    }

    // 我的
    @Query(() => User, { nullable: true })
    async me(
        @Ctx() { req, em }: MyContext
    ): Promise<User | null> {
        if (!req.session.userId) {
            return null
        }
        const user = await em.findOne(User, { id: req.session.userId })
        return user
    }
}